/**
 * 
 */
package org.tuestudent.grouppa;

import java.util.Date;

import org.apache.commons.lang3.StringEscapeUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.tuestudent.grouppa.AppContext.ActiveScreens;
import org.tuestudent.grouppa.GroupViewController.CreateGroupResponse;
import org.tuestudent.grouppa.GroupViewController.JoinGroupResponse;
import org.tuestudent.grouppa.ViewController.notificationIds;

/**
 * @author s121170 Class for handling the forum views and popups
 */
public class WallViewController extends ViewController {
    
    private static WallViewController instance;
    /**
     * the version of topics lastly read from database
     */
    private int lastTopicVersion=0;

    /**
     * ID to which all topic responses were read
     */
    private long lastCheckID=0;

    /**
     * for ignoring messages on first check
     */
    private boolean firstCheck= true;
    /**
     * hidden constructor, use getInstance()
     */
    private WallViewController() {

    }


    public static WallViewController getInstance() {
        if (instance != null)
            return instance;

        instance = new WallViewController();
        return instance;
    }

    /**
     * gives all topics from the forum Use this function every time to get all
     * the topics and comments This view controller will handle updating the
     * information
     * 
     * @return topics
     */
    public void getAllTopics() {
        // CHECK: get forum topics from server

        MessageFromApp message = new MessageFromApp();
        String username = Main.getContext().currentUserName;
        message.from = username;
        message.messageType = "getAllTopicsResponse";
        message.task = "getAllTopics";
        message.toController = "Forum";
        String groupName = Main.getContext().getCurrentGroup();
        message.toGroup = groupName;
        message.serializedArguments = "{\"groupName\":\"" + groupName + "\"}";
        Main.getConnection().sendMessage(message);
    }
    
    
    /**
     * gives all topics from the forum Use this function every time to get all
     * the topics and comments This view controller will handle updating the
     * information
     * 
     * @return topics
     */
    public void getChangedTopics() {
        // CHECK: get forum topics from server

        MessageFromApp message = new MessageFromApp();
        String username = Main.getContext().currentUserName;
        message.from = username;
        message.messageType = "getChangedTopicsResponse";
        message.task = "getAllTopics";
        message.toController = "Forum";
        String groupName = Main.getContext().getCurrentGroup();
        message.toGroup = groupName;
        message.serializedArguments = "{\"groupName\":\"" + groupName + "\"," +
        		"\"sinceVersion\":" + lastTopicVersion + "}";
        Main.getConnection().sendMessage(message);
    }

    public class GetAllTopicsResponse {

        public ForumTopic[] topics;
        public boolean succeeded;
        public String groupName;
    }

    public void getAllComments(int topicId) {
        // CHECK: get forum comments on that topic
        MessageFromApp message = new MessageFromApp();
        String username = Main.getContext().currentUserName;
        message.from = username;
        message.messageType = "getAllCommentsResponse";
        message.task = "getAllComments";
        message.toController = "Forum";
        String groupName = Main.getContext().getCurrentGroup();
        message.toGroup = groupName;
        message.serializedArguments = "{\"topicId\":" + topicId + "}";
        Main.getConnection().sendMessage(message);
    }
    
    /**
     * requests new comments from the server
     * WARNING: getAllComments should be run once before getNewComment
     * for the same topic
     * @param topicId topic for which you want comments
     */
    public void getNewComments(int topicId) {
        // CHECK: get forum comments on that topic
        MessageFromApp message = new MessageFromApp();
        String username = Main.getContext().currentUserName;
        message.from = username;
        message.messageType = "getNewCommentsResponse";
        message.task = "getAllComments";
        message.toController = "Forum";
        String groupName = Main.getContext().getCurrentGroup();
        message.toGroup = groupName;
        message.serializedArguments = "{\"topicId\":" + topicId + "," +
        		"\"sinceID\":" + lastCheckID + "}";
        Main.getConnection().sendMessage(message);
    }

    public class GetAllCommentsResponse {

        public int topicId;
        public ForumComment[] comments;
        public boolean succeeded;
        public String errorReason;
    }

    /**
     * adds a comment to specified topic
     * 
     * @param topicId
     *            Id of the topic to which you want to add the comment
     * @param comment
     *            comment to add to the topic
     */
    public void addComment(int topicId, ForumComment comment) {
        // CHECK: send message to server with comment and request adding the
        // comment
        MessageFromApp message = new MessageFromApp();
        String username = Main.getContext().currentUserName;
        message.from = username;
        message.messageType = "addCommentResponse";
        message.task = "addComment";
        message.toController = "Forum";
        String groupName = Main.getContext().getCurrentGroup();
        message.toGroup = groupName;
        message.serializedArguments =
                "{\"commentTopicId\":\"" + topicId + "\","
                        + "\"commentText\":\""
                        + StringEscapeUtils.escapeJson(comment.text) + "\","
                        + "\"groupName\":\"" + groupName + "\"}";
        Main.getConnection().sendMessage(message);

    }

    public class AddCommentResponse {

        public boolean succeeded;
        public String errorReason;
        public int topicId;
        /**
         * A comment to add with updated id
         */
        public ForumComment addedComment;
    }

    /**
     * adds a topic to the forum
     * 
     * @param topic
     *            topic to add to to the forum
     */
    public void addTopic(ForumTopic topic) {
        // TODO: send message to server to add a topic
        MessageFromApp message = new MessageFromApp();
        String username = Main.getContext().currentUserName;
        message.from = username;
        message.messageType = "addTopicResponse";
        message.task = "addTopic";
        message.toController = "Forum";
        String groupName = Main.getContext().getCurrentGroup();
        message.toGroup = groupName;
        message.serializedArguments =
                "{\"topicTitle\":\""
                        + StringEscapeUtils.escapeJson(topic.title) + "\","
                        + "\"topicIsPinned\":"
                        + (topic.isPinned ? "true" : "false") + ","
                        + "\"groupName\":\"" + groupName + "\","
                        + "\"topicText\":\""
                        + StringEscapeUtils.escapeJson(topic.text) + "\"}";
        Main.getConnection().sendMessage(message);
    }

    /**
     * changes an existing topic in the forum
     * 
     * @param topic
     *            a changed topic with a same id as an existant one
     */
    public void changeTopic(ForumTopic topic) {
        // CHECK: send message to server to change a topic
        MessageFromApp message = new MessageFromApp();
        AppContext ctx = Main.getContext();
        String username = ctx.currentUserName;
        if(!topic.author.equals(username) && !ctx.isManager())
        {
            Main.getContext().grouppaActivity.errorPopup("I am sorry, You cannot change that topic because you are not the author of it and you are not a manager of this group.");
            return;
        }
        message.from = username;
        message.messageType = "changeTopicResponse";
        message.task = "changeTopic";
        message.toController = "Forum";
        String groupName = ctx.getCurrentGroup();
        message.toGroup = groupName;
        message.serializedArguments =
                "{\"topicTitle\":\""
                        + StringEscapeUtils.escapeJson(topic.title) + "\","
                        + "\"topicIsPinned\":"
                        + (topic.isPinned ? "true" : "false") + ","
                        + "\"topicId\":"
                        + topic.id + ","
                        + "\"topicText\":\""
                        + StringEscapeUtils.escapeJson(topic.text) + "\"}";
        Main.getConnection().sendMessage(message);
    }
    public class AddTopicResponse {

        public boolean succeeded;
        public String errorReason;
        /**
         * Use this one to add to your view, it's id will be updated
         */
        public ForumTopic addedTopic;
    }

    public class ChangeTopicResponse {

        public boolean succeeded;
        public String errorReason;
        /**
         * Use this one to add to your view
         */
        public ForumTopic changedTopic;
    }

    @Override
    public void onMessageReceived(MessageFromServer message) {
        AppContext ctx = Main.getContext();
        if (ctx.activeScreen != ActiveScreens.grouppaScreen
                || checkForError(message))
            return; // ignoring messages if grouppa screen is not active
        if (message.messageType.equals("getAllTopicsResponse")) {
            processGetAllTopicsResponse(message, ctx);
        } else if (message.messageType.equals("getAllCommentsResponse")) {
            processGetAllCommentsResponse(message, ctx);
        } else if (message.messageType.equals("getNewCommentsResponse")) {
            processGetNewCommentsResponse(message, ctx);
        } else if (message.messageType.equals("getChangedTopicsResponse")) {
            processGetChangedTopicsResponse(message, ctx);
        } else if (message.messageType.equals("addCommentResponse")) {
            processAddCommentResponse(message, ctx);
        } else if (message.messageType.equals("addTopicResponse")) {
            processAddTopicResponse(message, ctx);
        } else if (message.messageType.equals("changeTopicResponse")) {
            processChangeTopicResponse(message, ctx);
        }
    }

    private void processGetAllTopicsResponse(MessageFromServer message,
            AppContext ctx) {
        if ( !(ctx.currentGrouppaFragment instanceof Wall))
            return;
        GetAllTopicsResponse response = new GetAllTopicsResponse();
        try {
            JSONObject args = new JSONObject(message.serializedArguments);
            response.groupName = args.optString("groupName");
            response.succeeded = args.optBoolean("succeeded");
            int version = args.getInt("maxVersion");
            lastTopicVersion = (version > lastTopicVersion)?version:lastTopicVersion;
            JSONArray topics = args.getJSONArray("topics");
            ForumTopic[] newTopics = readTopics(topics);
            response.topics = newTopics;
            ((Wall) ctx.currentGrouppaFragment)
                    .onGetAllTopicsResponse(response);
        } catch (Exception ex) {
            ctx.grouppaActivity.errorPopup("Error: " + ex.getMessage());
        }
    }

    private void processGetChangedTopicsResponse(MessageFromServer message,
            AppContext ctx) {
        if ( !(ctx.currentGrouppaFragment instanceof Wall))
        {
            try {
                JSONObject args = new JSONObject(message.serializedArguments);
                int version = args.getInt("maxVersion");
                lastTopicVersion = (version > lastTopicVersion)?version:lastTopicVersion;
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if(firstCheck)
                firstCheck = false;
            else
                showNotification("Grouppa Forum Message", "There is a new forum message for group "+ctx.getCurrentGroup(), ctx.getCurrentGroup(), notificationIds.forumMessageNotification);
            return;
        }
        GetAllTopicsResponse response = new GetAllTopicsResponse();
        try {
            JSONObject args = new JSONObject(message.serializedArguments);
            response.groupName = args.optString("groupName");
            response.succeeded = args.optBoolean("succeeded");
            int version = args.getInt("maxVersion");
            lastTopicVersion = (version > lastTopicVersion)?version:lastTopicVersion;
            JSONArray topics = args.getJSONArray("topics");
            ForumTopic[] newTopics = readTopics(topics);
            response.topics = newTopics;
            ((Wall) ctx.currentGrouppaFragment)
                    .onGetChangedTopicsResponse(response);
        } catch (Exception ex) {
            ctx.grouppaActivity.errorPopup("Error: " + ex.getMessage());
        }
    }


    /**
     * creates an array of ForumTopic from JSONArray with topics from server
     * @param topics JSONArray with data about topics
     * @return filled in array of ForumTopic
     * @throws JSONException
     */
    private ForumTopic[] readTopics(JSONArray topics) throws JSONException {
        ForumTopic[] newTopics = new ForumTopic[topics.length()];
        for (int i = 0; i < topics.length(); i++ ) {
            newTopics[i] = new ForumTopic();
            JSONObject topic = topics.getJSONObject(i);
            newTopics[i].author = topic.getString("author");
            newTopics[i].date = new Date(topic.getLong("date") * 1000);
            newTopics[i].id = topic.getInt("id");
            newTopics[i].isPinned = topic.getBoolean("isPinned");
            newTopics[i].responsesAmount = topic.getInt("commentsAmount");
            newTopics[i].text =
                    StringEscapeUtils.unescapeJson(topic.getString("text"));
            newTopics[i].title =
                    StringEscapeUtils
                            .unescapeJson(topic.getString("title"));
        }
        return newTopics;
    }

    private void processAddTopicResponse(MessageFromServer message,
            AppContext ctx) {
        if ( !(ctx.currentGrouppaFragment instanceof Wall))
            return;
        AddTopicResponse response = new AddTopicResponse();
        try {
            JSONObject args = new JSONObject(message.serializedArguments);
            response.succeeded = args.optBoolean("succeeded");
            if (response.succeeded) {
                JSONObject topic = args.getJSONObject("topic");
                ForumTopic newTopic = new ForumTopic();
                newTopic.isPinned = Boolean.parseBoolean(topic.getString("isPinned"));
                newTopic.author = topic.getString("author");
                newTopic.date = new Date(topic.getLong("date") * 1000);
                newTopic.id = Integer.parseInt(topic.getString("topicId"));
                newTopic.responsesAmount = 0;
                newTopic.text =
                        StringEscapeUtils.unescapeJson(topic.getString("text"));
                newTopic.title =
                        StringEscapeUtils.unescapeJson(topic.getString("title"));
                response.addedTopic = newTopic;
            }
            ((Wall) ctx.currentGrouppaFragment).onAddTopicResponse(response);
        } catch (Exception ex) {
            ctx.grouppaActivity.errorPopup("Error: " + ex.getMessage());
        }
    }

    private void processChangeTopicResponse(MessageFromServer message,
            AppContext ctx) {
        if ( !(ctx.currentGrouppaFragment instanceof Wall))
            return;
        ChangeTopicResponse response = new ChangeTopicResponse();
        try {
            JSONObject args = new JSONObject(message.serializedArguments);
            response.succeeded = args.optBoolean("succeeded");
            if (response.succeeded) {
                JSONObject topic = args.getJSONObject("topic");
                ForumTopic newTopic = new ForumTopic();
                newTopic.isPinned = Boolean.parseBoolean(topic.getString("isPinned"));
                newTopic.author = topic.getString("author");
                newTopic.date = new Date(topic.getLong("date") * 1000);
                newTopic.id = Integer.parseInt(topic.getString("id"));
                newTopic.responsesAmount = 0;
                newTopic.text =
                        StringEscapeUtils.unescapeJson(topic.getString("text"));
                newTopic.title =
                        StringEscapeUtils.unescapeJson(topic.getString("title"));
                response.changedTopic = newTopic;
            }
            response.errorReason = args.optString("reason");
            ((Wall) ctx.currentGrouppaFragment).onChangeTopicResponse(response);
        } catch (Exception ex) {
            ctx.grouppaActivity.errorPopup("Error: " + ex.getMessage());
        }
    }
    private void processAddCommentResponse(MessageFromServer message,
            AppContext ctx) {
        if ( !(ctx.currentGrouppaFragment instanceof ViewTopic))
            return;
        AddCommentResponse response = new AddCommentResponse();
        try {
            JSONObject args = new JSONObject(message.serializedArguments);
            response.succeeded = args.getBoolean("succeeded");
            response.topicId = args.optInt("topicId");
            if (response.succeeded) {
                JSONObject comment = args.getJSONObject("comment");
                ForumComment addedComment = new ForumComment();
                addedComment.author = comment.getString("author");
                addedComment.date = new Date(comment.getLong("date")*1000);
                addedComment.id = comment.getInt("commentId");
                addedComment.text =
                        StringEscapeUtils.unescapeJson(comment
                                .getString("text"));
                response.addedComment = addedComment;
            } else {
                response.addedComment = null;
            }
            ((ViewTopic) ctx.currentGrouppaFragment)
                    .onAddCommentResponse(response);

        } catch (Exception ex) {
            ctx.grouppaActivity.errorPopup("Error: " + ex.getMessage());
        }
    }

    private void processGetAllCommentsResponse(MessageFromServer message,
            AppContext ctx) {
        if ( !(ctx.currentGrouppaFragment instanceof ViewTopic))
            return;
        GetAllCommentsResponse response = new GetAllCommentsResponse();
        try {
            JSONObject args = new JSONObject(message.serializedArguments);
            response.topicId = args.optInt("topicId");
            response.succeeded = args.optBoolean("succeeded");
            JSONArray comments = args.getJSONArray("comments");
            lastCheckID=0;
            ForumComment[] newComments =
                    createForumCommentsFromJSONArray(comments);
            response.comments = newComments;
            ((ViewTopic) ctx.currentGrouppaFragment)
                    .onGetAllCommentsResponse(response);
        } catch (Exception ex) {
            ctx.grouppaActivity.errorPopup("Error: " + ex.getMessage());
        }
    }


    private ForumComment[] createForumCommentsFromJSONArray(JSONArray comments)
            throws JSONException {
        ForumComment[] newComments = new ForumComment[comments.length()];
        for (int i = 0; i < comments.length(); i++ ) {
            newComments[i] = new ForumComment();
            JSONObject comment = comments.getJSONObject(i);
            newComments[i].author = comment.getString("author");
            newComments[i].date = new Date(comment.getLong("date") * 1000);
            int id = comment.getInt("id");
            newComments[i].id = id;
            newComments[i].text =
                    StringEscapeUtils.unescapeJson(comment
                            .getString("text"));
            lastCheckID = (id > lastCheckID)?id:lastCheckID;
        }
        return newComments;
    }
    private void processGetNewCommentsResponse(MessageFromServer message,
            AppContext ctx) {
        if ( !(ctx.currentGrouppaFragment instanceof ViewTopic))
            return;
        GetAllCommentsResponse response = new GetAllCommentsResponse();
        try {
            JSONObject args = new JSONObject(message.serializedArguments);
            response.topicId = args.optInt("topicId");
            response.succeeded = args.optBoolean("succeeded");
            JSONArray comments = args.getJSONArray("comments");
            ForumComment[] newComments =
                    createForumCommentsFromJSONArray(comments);
            response.comments = newComments;
            ((ViewTopic) ctx.currentGrouppaFragment)
                    .onGetNewCommentsResponse(response);
        } catch (Exception ex) {
            ctx.grouppaActivity.errorPopup("Error: " + ex.getMessage());
        }
    }


    @Override
    public void reset() {
        lastCheckID = 0;
        lastTopicVersion = 0;
        firstCheck = true;
    }
}
